预备知识
本文通过代码实践来实现两种动态加载资源的方法:
- 通过 createPackageContext: 可以实现获取系统中已经安装的应用的资源
- 自定义 AssetManager:这种方法是插件化动态加载资源的原理
资源类
访问资源官方文档
我们知道,Android 程序中的每个资源在编译的时候,aapt 会生成 R 类,其中包含 res/ 目录中所有资源的资源 ID。 每个资源类型都有对应的 R 子类(例如,R.drawable 对应于所有可绘制对象资源),而该类型的每个资源都有对应的静态整型数(例如,R.drawable.icon)。这个整型数就是可用来检索资源的资源 ID。
资源ID 是一个32bit的数字,格式是PPTTNNNN , PP代表资源所属的包(package) ,TT代表资源的类型(type),NNNN代表这个类型下面的资源的名称。 对于系统资源来说取值是0x00-0x02,对于应用程序的资源来说,PP的取值是0×7f。
TT 和NNNN 的取值是由AAPT工具随意指定的–基本上每一种新的资源类型的数字都是从上一个数字累加的(从1开始)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72public final class R {
public static final class anim {
public static final int abc_fade_in=0x7f010000;
......
}
public static final class animator {
public static final int design_appbar_state_list_animator=0x7f020000;
}
public static final class attr {
public static final int actionBarDivider=0x7f030000;
......
}
public static final class bool {
public static final int abc_action_bar_embed_tabs=0x7f040000;
......
}
public static final class color {
public static final int abc_background_cache_hint_selector_material_dark=0x7f050000;
......
}
public static final class dimen {
public static final int abc_action_bar_content_inset_material=0x7f060000;
……
public static final int tooltip_y_offset_touch=0x7f06009b;
}
public static final class drawable {
public static final int abc_ab_share_pack_mtrl_alpha=0x7f070007;
......
}
public static final class id {
public static final int ALT=0x7f080000;
......
}
public static final class integer {
public static final int abc_config_activityDefaultDur=0x7f090000;
......
}
public static final class layout {
public static final int abc_action_bar_title_item=0x7f0a0000;
......
}
public static final class mipmap {
public static final int ic_launcher=0x7f0b0000;
......
}
public static final class string {
public static final int abc_action_bar_home_description=0x7f0c0000;
.....
}
public static final class style {
public static final int AlertDialog_AppCompat=0x7f0d0000;
.....
}
public static final class styleable {
public static final int[] ActionBar={
0x7f030031, 0x7f030032, 0x7f030033, 0x7f030061,
0x7f030062, 0x7f030063, 0x7f030064, 0x7f030065,
0x7f030066, 0x7f03006d, 0x7f030071, 0x7f030072,
0x7f03007d, 0x7f03009d, 0x7f03009e, 0x7f0300a2,
0x7f0300a3, 0x7f0300a4, 0x7f0300a9, 0x7f0300af,
0x7f0300f5, 0x7f0300fe, 0x7f03010e, 0x7f030112,
0x7f030113, 0x7f030137, 0x7f03013a, 0x7f030166,
0x7f030170
};
public static final int[] ActionBarLayout={
0x010100b3
};
……
public static final int View_theme=4;
}
}
createPackageContext 方法
再来了解一下 Context
中的 createPackageContext
方法,通过这个方法可以创建另外一个包的 Context
上下文,这样就可以访问该软件包的资源,甚至可以执行其它软件包的代码。
动态获取资源的两种方法
- 通过
getResources().getIdentifier(String name,String defType,String defPackage)
获取。 - 通过反射调用 R.java 类来获取。
动态加载已经安装的APK
代码实现
在测试的apk中添加字符串资源:
1 | <resources> |
并将该apk安装到手机上面。
在另外一个apk中实现下面的代码:
1 | public void clickTestDynamicLoadRes(View v) { |
结果:
1 | Test: str = 测试动态加载资源 |
成功实现字符串资源的动态加载,其它类型的资源实现方法一样。
上面例子中获取资源ID是通过反射R类来实现的,其实还有另外方法可以实现,通过 Resources
的 getIdentifier
方法类实现。
1 | public void clickTestDynamicLoadRes(View v) { |
动态加载未安装的APK
1 | public void clickTestDynamicLoadRes(View v) { |